﻿/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Author:       François PIETTE @ Balteau-NDT
Creation:     Feb 11, 2015
Description:  Demo showing how to automate Balteau NDT XRay generator control
              application (XRayRemoteControl.exe) using a TCP/IP connection.
Version:      1.00
Legal issues: This software is Open Source and subject to "The MIT License (MIT)"
              Copyright (C) 2015 Balteau-NDT
              Rue Voie de Liège 12, 4681 Hermalle Ss Argenteau, Belgium
              http://www.balteau-ndt.com
 
              Permission is hereby granted, free of charge, to any person 
              obtaining a copy of this software and associated documentation
              files (the "Software"), to deal in the Software without 
              restriction, including without limitation the rights to use, 
              copy, modify, merge, publish, distribute, sublicense, and/or 
              sell copies of the Software, and to permit persons to whom 
              the Software is furnished to do so, subject to the following 
              conditions:

              The above copyright notice and this permission notice shall 
              be included in all copies or substantial portions of the Software.

              THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
              EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
              OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
              NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
              HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
              WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
              OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
              DEALINGS IN THE SOFTWARE.

History:


 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Balteau
{
    public partial class BalteauXRayControlMainForm : Form
    {
        // Folder name for company
        static private string CompanyFolder = "Balteau NDT";
        static private string ProgName      = "BalteauXRayControl";

        // INI file layout
        static private string sectionWindow = "WindowMain";
        static private string keyTop        = "Top";
        static private string keyLeft       = "Left";
        static private string keyWidth      = "Width";
        static private string keyHeight     = "Height";
        static private string keyDisplay    = "DisplayHeight";
        
        private string exeName;         // Executable name
        private string localAppData;    // {UserProfile}\AppData special folder location
        private string iniFileName;     // Full path of INI file used to persist data

        private XRayRemoteClientTcp clientXRay;

        public BalteauXRayControlMainForm()
        {
            // INI file is located in {UserProfile}\AppData\{CompanyFolder}\{ExecutableName}
            exeName       = Process.GetCurrentProcess().ProcessName;
            localAppData  = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
                            Path.DirectorySeparatorChar + CompanyFolder;
            iniFileName   = localAppData + Path.DirectorySeparatorChar + 
                            System.IO.Path.GetFileNameWithoutExtension(exeName);
            Directory.CreateDirectory(iniFileName);  // Make sure the directory exists
            iniFileName   = iniFileName + Path.DirectorySeparatorChar + 
                            System.IO.Path.GetFileNameWithoutExtension(exeName) + ".ini";

            InitializeComponent();

            // Read the INI file to restore persistent data (Form's location and size)
            IniFile iniFile = new IniFile(iniFileName);
            int locTop     = iniFile.ReadInteger(sectionWindow, keyTop,  Location.Y);
            int locLeft    = iniFile.ReadInteger(sectionWindow, keyLeft, Location.X);
            int sizeWidth  = iniFile.ReadInteger(sectionWindow, keyWidth,   Size.Width);
            int sizeHeight = iniFile.ReadInteger(sectionWindow, keyHeight,  Size.Height);

            // If saved form's location and size are visible on any screen, restore it
            if (IsVisibleOnAnyScreen(new Rectangle(locLeft, locTop, sizeWidth, sizeHeight))) {
                StartPosition = FormStartPosition.Manual;
                Location      = new Point(locLeft, locTop);
                Size          = new Size(sizeWidth, sizeHeight);
            }
            // Read from INI file the height of the display panel
            splitContainer1.SplitterDistance = iniFile.ReadInteger(sectionWindow, keyDisplay, splitContainer1.SplitterDistance);

            // Init user interface display
            statusLabel.Text          = "DICONNECTED";
            errorLabel.Text           = "";
            kvNumericUpDown.Value     = 0;
            maNumericUpDown.Value     = 0;
            maSevenSegmentArray.Value = "";
            kvSevenSegmentArray.Value = "";
        }

        private void BalteauXRayControlMainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Write persistent data (Form's location and size) into INI file
            IniFile iniFile = new IniFile(iniFileName);
            iniFile.WriteInteger(sectionWindow, keyLeft,    Location.X);
            iniFile.WriteInteger(sectionWindow, keyTop,     Location.Y);
            iniFile.WriteInteger(sectionWindow, keyWidth,   Size.Width);
            iniFile.WriteInteger(sectionWindow, keyHeight,  Size.Height);
            iniFile.WriteInteger(sectionWindow, keyDisplay, splitContainer1.SplitterDistance);
        }

        // Check if a given rectangle (ie: form's location and size) is visible on any screen
        private bool IsVisibleOnAnyScreen(Rectangle rect)
        {
            foreach (Screen screen in Screen.AllScreens) {
                if (screen.WorkingArea.IntersectsWith(rect))
                    return true;
            }
            return false;
        }

        // This is called when the user move the splitter betwee the XRay interface
        // and the message area. We use it to center the XRay interface in the
        // available space.
        private void splitContainer1_Panel1_Resize(object sender, EventArgs e)
        {
            toolsPanel.Location = new Point(Math.Max(0, (splitContainer1.Panel1.Width  - toolsPanel.Width) / 2), 
                                            Math.Max(0, (splitContainer1.Panel1.Height - toolsPanel.Height) / 2));
        }

        // Display a message in the message pane
        private void Display(string Msg)
        {
            DisplayTextBox.AppendText(Msg);
            DisplayTextBox.AppendText("\n");
        }

        // This event handler is called when the form is shown on screen
        private void BalteauXRayControlMainForm_Shown(object sender, EventArgs e)
        {
            // Create an instance of Balteau NDT generator controller
            clientXRay                  = new XRayRemoteClientTcp();

            // Connect event handlers to the controller
            clientXRay.Display         += ClientXRay_Display;
            clientXRay.Connected       += ClientXRay_Connected;
            clientXRay.Disconnected    += ClientXRay_Disconnected;
            clientXRay.KVUpdate        += ClientXRay_KVUpdate;
            clientXRay.MAUpdate        += ClientXRay_MAUpdate;
            clientXRay.CommandDone     += ClientXRay_CommandDone;
            clientXRay.GenStatusUpdate += ClientXRay_GenStatusUpdate;
            clientXRay.GenErrorUpdate  += clientXRay_GenErrorUpdate;
        }

        // This event handler is called when the generator updates his error state
        void clientXRay_GenErrorUpdate(object sender, GenErrorUpdateEventArgs e)
        {
            errorLabel.Text = e.error.ToString();
        }

        // This event handler is called when the generator updates his status
        void ClientXRay_GenStatusUpdate(object sender, GenStatusUpdateEventArgs e)
        {
            statusLabel.Text = e.status.ToString();
        }

        // This event handler is called when the generator updates his kV value
        void ClientXRay_KVUpdate(object sender, KVUpdateEventArgs e)
        {
            kvSevenSegmentArray.Value = e.kv.ToString();
        }

        // This event handler is called when the generator updates his mA value
        void ClientXRay_MAUpdate(object sender, MAUpdateEventArgs e)
        {
            string V = ParamParsing.MyFloatToStr(e.ma, 0, 1, false);
            maSevenSegmentArray.Value = V;
        }

        // This event handler is called when the generator controller class has
        // done executing a command. This is the place to update user interface.
        void ClientXRay_CommandDone(object sender, CommandDoneEventArgs e)
        {
            if (string.Equals(e.cmd, "GetState")) {
                kvNumericUpDown.Value     = clientXRay.kvSet;
                maNumericUpDown.Value     = Convert.ToDecimal(clientXRay.maSet);
                kvSevenSegmentArray.Value = clientXRay.kvRead.ToString();
                maSevenSegmentArray.Value = ParamParsing.MyFloatToStr(clientXRay.maRead);
                statusLabel.Text          = clientXRay.genStatus.ToString();
                errorLabel.Text           = clientXRay.genError.ToString();
            }
            else if (string.Equals(e.cmd, "StartX")) {
                if (clientXRay.genStatus.Value == XRayStatus.Status.xrsSTBPRH)
                    MessageBox.Show("Preheating required", ProgName, 
                                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // This event handler is called when the generator controller class wants
        // to display an informative message (for debugging purpose only, don't base
        // actions on those messages).
        void ClientXRay_Display(object sender, DisplayEventArgs e)
        {
            Display(e.msg);
        }

        // This event handler is called when the generator controller class has
        // established the connection with XRayRemoteControl application
        void ClientXRay_Connected(object sender, ConnectedEventArgs e)
        {
            if (e.errCode == 0) {
                Display("Connected");
                statusLabel.Text = "CONNECTED";
                errorLabel.Text  = "";
                clientXRay.GetState();
            }
            else {
                statusLabel.Text = "CONNECTION FAILED";
                errorLabel.Text  = e.errCode.ToString();
                Display("Unabled to connect. ErrCode=" + e.errCode.ToString());
            }
        }

        // This event handler is called when the generator controller class has
        // lost or closed the connection with XRayRemoteControl application
        void ClientXRay_Disconnected(object sender, DisconnectedEventArgs e)
        {
            if (e.errCode == 0)
                Display("Disconnected");
            else
                Display("Disconnected. ErrCode=" + e.errCode.ToString());
            // Clear displayed value
            maSevenSegmentArray.Value = "";
            kvSevenSegmentArray.Value = "";
            statusLabel.Text          = "DISCONNECTED";
            errorLabel.Text           = "";
        }

        // This is called when the user clicks on the Start button
        private void StartXButton_Click(object sender, EventArgs e)
        {
            if (clientXRay.connected)
                clientXRay.StartX("");
            else
                MessageBox.Show("Not connected yet", ProgName, 
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        // This is called when the user clicks on the Stop button
        private void StopXButton_Click(object sender, EventArgs e)
        {
            if (clientXRay.connected)
                clientXRay.StopX();
            else
                MessageBox.Show("Not connected yet", ProgName, 
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        // This is called when the user clicks on the Connect button
        private void ConnectButton_Click(object sender, EventArgs e)
        {
            clientXRay.port      = Convert.ToInt32(portTextBox.Text);
            clientXRay.ipAddress = ipTextBox.Text;
            clientXRay.Connect();
        }

        // This is called when the user clicks on the Disconnect button
        private void DisconnectButton_Click(object sender, EventArgs e)
        {
            clientXRay.Disconnect();
        }

        // This is called when the user change kV settings
        private void kvNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            if (clientXRay.connected)
                clientXRay.SetKVMA((int)kvNumericUpDown.Value, (double)maNumericUpDown.Value);
        }

        // This is called when the user change mA settings
        private void maNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            if (clientXRay.connected)
                clientXRay.SetKVMA((int)kvNumericUpDown.Value, (double)maNumericUpDown.Value);
        }

    }
}
